#pragma once
#include <string>
#include <cstring>
#include <functional>
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
#include "LockGuard.hpp"

using namespace std;

using task_t = function<void()>;

class MessageRoute
{
private:
    // 判断用户addr是否存在于在线用户表中
    bool IsExists(const InetAddr& addr)
    {
        for (auto a : _online_user)
        {
            if (a == addr)
                return true;
        }
        return false;
    }

public:
    MessageRoute()
    {
        pthread_mutex_init(&_mutex, nullptr);
    }
    // 增添聊天用户
    void AddUser(const InetAddr& addr)
    {
        LockGuard lockguard(&_mutex);
        if (IsExists(addr))
        {
            return;
        }
        _online_user.push_back(addr);
    }
    // 将用户消息发送到信息界面，也是线程池中的任务函数
    void RouteHelper(int sockfd, string message, InetAddr allclient)
    {
        LockGuard lockguard(&_mutex);
        struct sockaddr_in addr = allclient.Addr();
        for (auto u : _online_user)
        {
            sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&addr, sizeof(addr));
        }
    }
    // 路由信息转发，即构建函数对象（也可以说建立任务），将其入队列，让线程池进行任务转发
    void Route(int sockfd, string send_message, InetAddr who, InetAddr allclient)
    {
        // 用户首次发消息时，需要将用户自己插入到在线用户表中
        AddUser(who);
        // 注意：由于RouteHelper存在参数，而任务task_t是没有参数的，这里直接bind绑定固定参数，这时传递t就相当于没有参数了
        // bind与function经常连用，这里要注意如何灵活运用其参数和配合使用 
        task_t t = bind(&MessageRoute::RouteHelper, this, sockfd, send_message, allclient);
        ThreadPool<task_t>::GetInstance()->Enqueue(t);
    }
    ~MessageRoute()
    {
        pthread_mutex_destroy(&_mutex);
    }

private:
    vector<InetAddr> _online_user; // 在线用户表
    pthread_mutex_t _mutex;
};